package cn.unknowpupil.init.configure.oauth;

import cn.hutool.core.collection.CollectionUtil;
import cn.unknowpupil.init.async.AsyncFactory;
import cn.unknowpupil.init.async.AsyncManager;
import cn.unknowpupil.init.common.constant.CacheKeyConstants;
import cn.unknowpupil.init.common.enums.LoginEnum;
import cn.unknowpupil.init.common.enums.ResultStatus;
import cn.unknowpupil.init.domain.dto.LoginUser;
import cn.unknowpupil.init.domain.dto.PermissionNode;
import cn.unknowpupil.init.domain.dto.UsernamePasswordLoginDto;
import cn.unknowpupil.init.domain.system.SysUser;
import cn.unknowpupil.init.exceptions.HealthException;
import cn.unknowpupil.init.properties.SecurityProperties;
import cn.unknowpupil.init.service.system.ISysPermissionService;
import cn.unknowpupil.init.service.system.ISysRoleService;
import cn.unknowpupil.init.service.system.ISysUserService;
import cn.unknowpupil.init.utils.redis.RedisUtils;
import cn.unknowpupil.init.utils.security.SpringUtils;
import cn.unknowpupil.init.utils.strings.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.User;
import org.apache.ibatis.cache.CacheKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Created at 2020/10/20 by w10g <br>
 * Initialize UsernamePasswordProvider ... <br>
 * 自定义账户密码登录
 *
 * @author unknowpupil
 * @since 0.0.1
 */
@Slf4j
@Component
public class UsernamePasswordProvider implements AuthenticationProvider {


    @Autowired
    private ISysUserService userService;
    @Autowired
    private SecurityProperties securityProperties;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private ISysRoleService roleService;

    @Autowired
   private AsyncManager asyncManager;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 同为security 这个东西不能再注入了，只能在spring容器中获取
        BCryptPasswordEncoder bCryptPasswordEncoder = SpringUtils.getBean(BCryptPasswordEncoder.class);
        log.info("自定义账户密码登录");

        UsernamePasswordLoginDto dto = (UsernamePasswordLoginDto) authentication.getPrincipal();

        String code = (String) redisUtils.get(securityProperties.getCaptchaCodeKey() + dto.getUuid());

        if (StringUtils.isBlank(code)) {
            asyncManager.execute(AsyncFactory.recordLoginInfo( dto.getUsername(), LoginEnum.FAILURE, ResultStatus.CAPTCHA_EXIST_OR_EXPIRED.message()));

            throw new HealthException(ResultStatus.CAPTCHA_EXIST_OR_EXPIRED);
        }
        if (StringUtils.isBlank(dto.getCode()) || !dto.getCode().equalsIgnoreCase(code)) {
            asyncManager.execute(AsyncFactory.recordLoginInfo(dto.getUsername(), LoginEnum.FAILURE,ResultStatus.CAPTCHA_ERROR.message()));
            throw new HealthException(ResultStatus.CAPTCHA_ERROR);
        }
        // 清除验证码
        redisUtils.del(securityProperties.getCaptchaCodeKey() + dto.getUuid());

        // 查询用户 判断密码


        SysUser user = userService.getUserByUsername(dto.getUsername());
       if (Objects.isNull(user)) {
            asyncManager.execute(AsyncFactory.recordLoginInfo(dto.getUsername(), LoginEnum.FAILURE,ResultStatus.USER_ERROR.message()));
            throw new HealthException(ResultStatus.USER_PASSWORD_ERROR);
        }
        if (!bCryptPasswordEncoder.matches(dto.getPassword(), user.getPassword())) {
            asyncManager.execute(AsyncFactory.recordLoginInfo(dto.getUsername(), LoginEnum.FAILURE,ResultStatus.USER_PASSWORD_ERROR.message()));
            throw new HealthException(ResultStatus.USER_PASSWORD_ERROR);
        }
        Set<String> menuPermission = roleService.getMenuPermission(user);
        if(menuPermission.size()==0){
            asyncManager.execute(AsyncFactory.recordLoginInfo(dto.getUsername(), LoginEnum.FAILURE,ResultStatus.USER_NOT_ROLE.message()));
            throw new HealthException(ResultStatus.USER_NOT_ROLE);
        }
        Set<SimpleGrantedAuthority> collect = menuPermission.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toSet());

        LoginUser loginUser = new LoginUser(user.getId(),user.getPid(), user.getUserName(), user.getPassword(), true, collect);
        asyncManager.execute(AsyncFactory.recordLoginInfo(dto.getUsername(), LoginEnum.SUCCESS,ResultStatus.SUCCESS.message()));
        return new UsernamePasswordLoginAuthentication(loginUser, "", collect);
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return UsernamePasswordLoginAuthentication.class.isAssignableFrom(aClass);
    }

}
